﻿using FirstFloor.ModernUI.Presentation;
using FirstFloor.ModernUI.Windows.Controls;
using FirstFloor.ModernUI.Windows.Navigation;
using LinnerToolkit.Core.Codec;
using LinnerToolkit.Desktop.Codec;
using LinnerToolkit.Desktop.ModernUI.Mvvm;
using LinnerToolkit.Desktop.ModernUI.Navigation;
using LinnerToolkit.MindBody.Core;
using Mina.Core.Future;
using Mina.Filter.Codec;
using Mina.Transport.Serial;
using SimulationPTSystem.DAL;
using SimulationPTSystem.Evaluation.Codec;
using SimulationPTSystem.Evaluation.Models;
using SimulationPTSystem.ExpressionReport;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Linq;
using System.Windows;
using System.Windows.Input;

namespace SimulationPTSystem.Evaluation.ViewModels
{
    public class TrainingPageViewModel : ModernViewModelBase
    {
        private ObservableCollection<ChengYuanInfo> chengYuanInfos;
        private ObservableCollection<Trainer> trainerCollection;
        private TeamInfo teamInfo;

       public List<SerialConnector> Connectors { get; protected set; }

        /// <summary>
        /// 流程控制器
        /// </summary>
        public IProcessController ProcessController { get; }
        /// <summary>
        /// 当前训练团队信息
        /// </summary>
        public CurrentTrainingTeamInfo CurrentTrainingTeamInfo { get; protected set; }
        /// <summary>
        /// 团队信息
        /// </summary>
        public TeamInfo TeamInfo { get => teamInfo; set => Set(ref teamInfo,value); }
        /// <summary>
        /// 成员信息集合
        /// </summary>
        public ObservableCollection<ChengYuanInfo> ChengYuanInfos { get => chengYuanInfos; set => Set(ref chengYuanInfos, value); }

        /// <summary>
        /// 训练人员集合
        /// </summary>
        public ObservableCollection<Trainer> TrainerCollection { get => trainerCollection; set => Set(ref trainerCollection, value); }
        /// <summary>
        /// 训练人员集合字典
        /// </summary>
        protected Dictionary<byte, Trainer> _trainerDic;

        public ICommand AddChengYuanCommand { get; }

        public ICommand AddAllChengYuanCommand { get; }

        public ICommand StartCommand { get; }

        public TrainingPageViewModel(IModernNavigationService navigationService) : base(navigationService)
        {
            ProcessController = new DefaultProcessController();
            ProcessController.PositionChanged += ProcessController_PositionChanged;

            Connectors = new List<SerialConnector>();

            TrainerCollection = new ObservableCollection<Trainer>();
            _trainerDic = new Dictionary<byte, Trainer>();

            AddChengYuanCommand = new RelayCommand((obj) =>
              {
                  if (obj is IList selectedItems)
                  {
                      if (selectedItems == null || selectedItems.Count == 0)
                          return;

                      foreach (var chengYuanInfo in selectedItems.Cast<ChengYuanInfo>().ToList())
                      {
                          if (ChengYuanInfos.Contains(chengYuanInfo))
                          {
                              AddTrainer(chengYuanInfo);
                              ChengYuanInfos.Remove(chengYuanInfo);
                          }
                      }
                  }
              });

            AddAllChengYuanCommand = new RelayCommand((obj) =>
              {
                  if (ChengYuanInfos.Count == 0)
                      return;

                  foreach (var chengYuanInfo in ChengYuanInfos)
                  {
                      AddTrainer(chengYuanInfo);
                  }
                  ChengYuanInfos.Clear();
              });

            StartCommand = new RelayCommand((obj) =>
              {
                  if (ProcessController.IsStarted)
                  {
                      if (ModernDialog.ShowMessage("是否要结束训练？", "提示", MessageBoxButton.OKCancel) != MessageBoxResult.OK)
                          return;

                      Stop();
                      SaveData();

                      if (!ExpressionReport.Common.SysCommon.Init()) return;
                      FrmMainVideoMonitor frmMainVideoMonitor = new FrmMainVideoMonitor();
                      frmMainVideoMonitor.ShowDialog();

                      TrainerCollection.Clear();

                      // 删除当前训练团队信息
                      SimulationPTContext.Instance.CurrentTrainingTeamInfo.Remove(CurrentTrainingTeamInfo);
                      SimulationPTContext.Instance.SaveChanges();
                      navigationService.GoBack();
                  }
                  else
                  {
                      if (TrainerCollection.Count <= 0)
                      {
                          ModernDialog.ShowMessage("请先添加要进行训练的人员", "提示", MessageBoxButton.OK);
                          return;
                      }
                      Start();
                  }
              });
        }

        private void SaveData()
        {
            if (TrainerCollection.Count > 0)
            {
                TeamTrainingRec teamTrainingRec = new TeamTrainingRec()
                {
                    TeamTrainingRecUUID = CurrentTrainingTeamInfo.TeamTrainingRecUUID,
                    StartTime = CurrentTrainingTeamInfo.StartTime,
                    TeamInfoID = CurrentTrainingTeamInfo.TeamInfoId,
                    ComplexTrainingDuration = ProcessController.Position,
                };

                List<ChengYuanTrainingRec> chengYuanTrainingRecs = new List<ChengYuanTrainingRec>();
                foreach(var trainer in trainerCollection)
                {
                    ChengYuanTrainingRec rec = trainer.Save();
                    rec.TrainingDate = teamTrainingRec.StartTime;
                    rec.TeamTrainingRecUUID = CurrentTrainingTeamInfo.TeamTrainingRecUUID;
                    rec.ComplexTrainingDuration = teamTrainingRec.ComplexTrainingDuration;
                    chengYuanTrainingRecs.Add(rec);
                }
                teamTrainingRec.ChengYuanTrainingRecs = chengYuanTrainingRecs;

                SimulationPTDAL.AddTeamTrainingRec(teamTrainingRec);
            }
        }

        private void ProcessController_PositionChanged(object sender, ProcessPositionChangedEventArgs e)
        {
            if (e.Position.Seconds % 10 == 0)
            {
                try
                {
                    for (int i = 0; i < TrainerCollection.Count; i++)
                    {
                        TrainerCollection[i].UpdateData(e.Position);
                    }
                }
                catch (Exception ex)
                {
                    Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        ModernDialog.ShowMessage($"PositionChanged. Message: {ex.Message}, Stacktrace: {ex.StackTrace}", "错误", MessageBoxButton.OK);
                    }));
                }
            }
        }

        protected void AddTrainer(ChengYuanInfo chengYuanInfo)
        {
            if (!_trainerDic.ContainsKey((byte)chengYuanInfo.SensorDeviceID))
            {
                Trainer trainer = new Trainer(chengYuanInfo);
                TrainerCollection.Add(trainer);
                _trainerDic.Add((byte)chengYuanInfo.SensorDeviceID, trainer);
            }
        }

        public void Start()
        {
            if (!ProcessController.IsStarted)
            {
                string[] portNames = ConfigurationManager.AppSettings["PortName"].Split(',');
                if (portNames.Length == 0)
                {
                    ModernDialog.ShowMessage("端口配置错误！", "提示", MessageBoxButton.OK);
                    return;
                }

                if (!int.TryParse(ConfigurationManager.AppSettings["BaudRate"], out int baudRate))
                {
                    ModernDialog.ShowMessage("波特率设置错误！", "提示", MessageBoxButton.OK);
                    return;
                }

                foreach (var portName in portNames)
                {
                    var connector = new SerialConnector();
                    connector.FilterChain.AddLast("codec", new ProtocolCodecFilter(new DefaultDemuxingProtocolCodecFactory(new DataDecoder(), new ByteDataEncoder())));
                    IConnectFuture future = connector.Connect(new SerialEndPoint(portName, baudRate, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One));
                    future.Await();
                    if (!future.Connected)
                    {
                        foreach (var c in Connectors)
                        {
                            foreach (var session in c.ManagedSessions.Values)
                            {
                                if (session.Connected)
                                    session.Close(true);
                            }
                        }
                        Connectors.Clear();
                        ModernDialog.ShowMessage("连接接收器失败", "提示", System.Windows.MessageBoxButton.OK);
                        return;
                    }
                    else
                        Connectors.Add(connector);
                }

                foreach (var c in Connectors)
                {
                    c.MessageReceived += Connector_MessageReceived;
                    c.ExceptionCaught += C_ExceptionCaught;
                }

                foreach (var trainer in TrainerCollection)
                    trainer.StartTraining();
                ProcessController.Reset();
                ProcessController.Start();
            }
        }

        private void C_ExceptionCaught(object sender, Mina.Core.Session.IoSessionExceptionEventArgs e)
        {
            Application.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                string message = string.Empty;
                if (e.Session.LocalEndPoint != null)
                    message = $"Session: { e.Session.LocalEndPoint.ToString()}, ";
                ModernDialog.ShowMessage($"{message}Exception Message: {e.Exception.Message}, Stacktrace: {e.Exception.StackTrace}", "错误", MessageBoxButton.OK);
            }));
        }

        private void Connector_MessageReceived(object sender, Mina.Core.Session.IoSessionMessageEventArgs e)
        {
            try
            {
                if (e.Message is SensorData sensorData)
                {
                    if (_trainerDic.ContainsKey(sensorData.MachineID))
                        _trainerDic[sensorData.MachineID].GetData(sensorData, ProcessController.Position);
                }
            }
            catch(Exception ex)
            {
                Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                {
                    ModernDialog.ShowMessage($"MessageReceived. Message: {ex.Message}, Stacktrace: {ex.StackTrace}", "错误", MessageBoxButton.OK);
                }));
            }
        }

        public void Stop()
        {
            ProcessController.Stop();
            foreach (var trainer in TrainerCollection)
                trainer.StopTraining();
        }

        public override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            if (!(e.Parameter is CurrentTrainingTeamInfo currentTrainingTeamInfo))
                throw new ArgumentNullException("CurrentTrainingTeamInfo cannot be null");

            CurrentTrainingTeamInfo = currentTrainingTeamInfo;
            if (CurrentTrainingTeamInfo.TeamInfo == null)
                throw new ArgumentNullException("TeamInfo cannot be null");

            TeamInfo = CurrentTrainingTeamInfo.TeamInfo;
            ChengYuanInfos = new ObservableCollection<ChengYuanInfo>(TeamInfo.ChengYuanInfos);
        }

        public override void OnNavigatedFrom(NavigationEventArgs e)
        {
            base.OnNavigatedFrom(e);

            TrainerCollection.Clear();
            _trainerDic.Clear();

            ProcessController.Stop();
            ProcessController.Reset();

            foreach (var connector in Connectors)
            {
                connector.MessageReceived -= Connector_MessageReceived;
                connector.ExceptionCaught -= C_ExceptionCaught;
                foreach (var session in connector.ManagedSessions.Values)
                {
                    if (session.Connected)
                        session.Close(true);
                }
                connector.Dispose();
            }
            Connectors.Clear();
        }
    }
}
